/*
 * mana (compiler)
 * Copyright (c) 2003 Shun Moriya <shun@mnu.sakura.ne.jp>
 *
 * The MIT License
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*!
 * @file
 * @brief	文字列格納に関するソースファイル
 *
 * このファイルは文字列を高速に参照するためのハッシュに関係する
 * ソースファイルです。
 *
 * @author	Shun Moriya <shun@mnu.sakura.ne.jp>
 */

#if !defined(___MANA_MALLOC_H___)
#include "../library/mana_malloc.h"
#endif
#if !defined(___MANA_POOL_H___)
#include "mana_pool.h"
#endif
#include <string.h>

/*! @brief String hash table size */
#define MANA_POOL_HASH_TABLE_SIZE	(887)

/*! @brief String hash table type */
typedef struct mana_pool_string_hash_table
{
	char* name;									/*!< 文字列 */
	int length;									/*!< 文字列の長さ */
	struct mana_pool_string_hash_table* next;	/*!< @brief 次のmana_pool_string_hash_table */
} mana_pool_string_hash_table;

/*! @brief String hash table */
static mana_pool_string_hash_table *s_mana_pool_hash_table[MANA_POOL_HASH_TABLE_SIZE];

/*!
 * @brief		ハッシュ値を取得
 * @param[in]	string		文字列
 * @return		ハッシュ値
 */
static int mana_pool_get_hash_value(char* string)
{
	unsigned h, g;

	for(h = 0; *string != '\0'; string++)
	{
		h = (h << 4) + (unsigned)(*string);
		g = h & 0xf0000000;
		if(g)
			h = (h ^ g >> 24) ^ g;
	}

	return(h % MANA_POOL_HASH_TABLE_SIZE);
}

#if 0
// ハッシュキーを作る
GzUInt32 hashstr(const char* str, size_t len)
{
	GzUInt32 h	= len;
	size_t step = (len>>5) | 1;

	for(int i=0; i<=len; i += step )
	{
		h = h ^ ((h<<5)+(h>>2)+(unsigned short)*(str++));
	}
	return h;
}
#endif

/*!
 */
void mana_pool_initialize(void)
{
	int i;

	for(i = 0; i < MANA_POOL_HASH_TABLE_SIZE; i ++)
	{
		s_mana_pool_hash_table[i] = 0;
	}
}

/*!
 */
void mana_pool_finalize(void)
{
	int i;

	for(i = 0; i < MANA_POOL_HASH_TABLE_SIZE; i++)
	{
		mana_pool_string_hash_table* hash;
		hash = s_mana_pool_hash_table[i];
		while(hash != 0)
		{
			mana_pool_string_hash_table* pNext;
			pNext = hash->next;
			if(hash->name)
				mana_free(hash->name);
			mana_free(hash);
			hash = pNext;
		}
		s_mana_pool_hash_table[i] = 0;
	}
}

/*!
 * @param[in]	string	文字列
 * @return
 * 文字列のアドレス。
 * 重複していた場合は設定済みの文字列のアドレスが返る。
 * 設定失敗の場合はNULLが返る。
 */
char* mana_pool_set(char* string)
{
	mana_pool_string_hash_table* hash;
	int hash_value;
	int length;

	if(string == NULL)
		return NULL;

	hash_value = mana_pool_get_hash_value(string);
	length = strlen(string);

	for(hash = s_mana_pool_hash_table[hash_value]; hash != 0; hash = hash->next)
	{
		if((hash->length) == length && strcmp(hash->name, string) == 0)
		{
			return hash->name;
		}
	}
	hash = (mana_pool_string_hash_table*)mana_malloc(sizeof(mana_pool_string_hash_table));
	if(hash)
	{
		hash->name = (char*)mana_malloc(length + 1);
		if(hash->name)
		{
			hash->length = length;
			hash->next = s_mana_pool_hash_table[hash_value];
			s_mana_pool_hash_table[hash_value] = hash;

#if defined(__STDC_WANT_SECURE_LIB__)
			strcpy_s(hash->name, length + 1, string);
#else
			strcpy(hash->name, string);
#endif
			return hash->name;
		}
		else
		{
			mana_free(hash);
		}
	}

	return NULL;
}
